/**
 * axiosTamperMonkeyAdapter.js
 * axios GM_xmlhttpRequest adapter
 *
 * support axios version: axios@0.24.0
 * usage when loaded:
 * axios.defaults.adapter = xhrAdapter;
 * @param config
 * @returns {Promise<void>}
 * @author http://mmbro.gitee.com
 * @since 202217
 * @version 1.0.0
 */

var originAdapter = null;
if (!!axios && !!axios.defaults && !!axios.defaults.adapter) {
    originAdapter = axios.defaults.adapter;
}

// 配置依赖兼容隔离
if (typeof (appConfig) === 'undefined') {
    appConfig = {
        isDebug: true,
        axiosTimeOut: 60 * 1000,
    };
    if (typeof (unsafeWindow) !== 'undefined') {
        console.log('unsafeWindow is undefined, set up appConfig');
        unsafeWindow.appConfig = appConfig;
    }
}

// 配置依赖兼容隔离
if (typeof (isJson) === 'undefined') {
    /**
     * 判断字符串是否为json(xhrAdapter依赖)
     * @param str
     * @returns {boolean}
     * @see <a href="https://www.cnblogs.com/lanleiming/p/7096973.html">【最简单的方法】js判断字符串是否为JSON格式（20180115更新）</a>
     */
    window.isJson = (str) => {
        if (typeof str === 'string') {
            try {
                let obj = JSON.parse(str);
                return !!(typeof obj === 'object' && obj);
            } catch (e) {
                console.log('isJson error： %o !!!', str, e);
            }
        }
        return false;
    };
}

function xhrAdapter(config) {
    // before keep no change for axios origin code.
    return new Promise(function dispatchXhrRequest(resolve, reject) {
        var requestData = config.data;
        var requestHeaders = config.headers;
        var responseType = config.responseType;
        var overrideMimeType = config.overrideMimeType;
        var onCanceled;

        // 全局超时设置
        if (!config.timeout) {
            config.timeout = appConfig.axiosTimeOut;
            if (appConfig.isDebug) {
                console.debug(`超时全局配置=${config.timeout}`);
            }
        }
        // var request = new XMLHttpRequest();
        //
        // // HTTP basic authentication
        // if (config.auth) {
        //     var username = config.auth.username || '';
        //     var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
        //     requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
        // }
        //
        // var fullPath = buildFullPath(config.baseURL, config.url);
        // request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
        //
        // // Set the request timeout in MS
        // request.timeout = config.timeout;
        //
        // function onloadend() {
        //     if (!request) {
        //         return;
        //     }
        //     // Prepare the response
        //     var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
        //     var responseData = !responseType || responseType === 'text' || responseType === 'json' ?
        //         request.responseText : request.response;
        //     var response = {
        //         data: responseData,
        //         status: request.status,
        //         statusText: request.statusText,
        //         headers: responseHeaders,
        //         config: config,
        //         request: request
        //     };
        //
        //     settle(function _resolve(value) {
        //         resolve(value);
        //         done();
        //     }, function _reject(err) {
        //         reject(err);
        //         done();
        //     }, response);
        //
        //     // Clean up request
        //     request = null;
        // }
        //
        // if ('onloadend' in request) {
        //     // Use onloadend if available
        //     request.onloadend = onloadend;
        // } else {
        //     // Listen for ready state to emulate onloadend
        //     request.onreadystatechange = function handleLoad() {
        //         if (!request || request.readyState !== 4) {
        //             return;
        //         }
        //
        //         // The request errored out and we didn't get a response, this will be
        //         // handled by onerror instead
        //         // With one exception: request that using file: protocol, most browsers
        //         // will return status as 0 even though it's a successful request
        //         if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
        //             return;
        //         }
        //         // readystate handler is calling before onerror or ontimeout handlers,
        //         // so we should call onloadend on the next 'tick'
        //         setTimeout(onloadend);
        //     };
        // }
        //
        // // Handle browser request cancellation (as opposed to a manual cancellation)
        // request.onabort = function handleAbort() {
        //     if (!request) {
        //         return;
        //     }
        //
        //     reject(createError('Request aborted', config, 'ECONNABORTED', request));
        //
        //     // Clean up request
        //     request = null;
        // };
        //
        // // Handle low level network errors
        // request.onerror = function handleError() {
        //     // Real errors are hidden from us by the browser
        //     // onerror should only fire if it's a network error
        //     reject(createError('Network Error', config, null, request));
        //
        //     // Clean up request
        //     request = null;
        // };
        //
        // // Handle timeout
        // request.ontimeout = function handleTimeout() {
        //     var timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
        //     var transitional = config.transitional || defaults.transitional;
        //     if (config.timeoutErrorMessage) {
        //         timeoutErrorMessage = config.timeoutErrorMessage;
        //     }
        //     reject(createError(
        //         timeoutErrorMessage,
        //         config,
        //         transitional.clarifyTimeoutError ? 'ETIMEDOUT' : 'ECONNABORTED',
        //         request));
        //
        //     // Clean up request
        //     request = null;
        // };
        //
        // // Add xsrf header
        // // This is only done if running in a standard browser environment.
        // // Specifically not if we're in a web worker, or react-native.
        // if (utils.isStandardBrowserEnv()) {
        //     // Add xsrf header
        //     var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
        //         cookies.read(config.xsrfCookieName) :
        //         undefined;
        //
        //     if (xsrfValue) {
        //         requestHeaders[config.xsrfHeaderName] = xsrfValue;
        //     }
        // }
        //
        // // Add headers to the request
        // if ('setRequestHeader' in request) {
        //     utils.forEach(requestHeaders, function setRequestHeader(val, key) {
        //         if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
        //             // Remove Content-Type if data is undefined
        //             delete requestHeaders[key];
        //         } else {
        //             // Otherwise add header to the request
        //             request.setRequestHeader(key, val);
        //         }
        //     });
        // }
        //
        // // Add withCredentials to request if needed
        // if (!utils.isUndefined(config.withCredentials)) {
        //     request.withCredentials = !!config.withCredentials;
        // }
        //
        // // Add responseType to request if needed
        // if (responseType && responseType !== 'json') {
        //     request.responseType = config.responseType;
        // }
        //
        // // Handle progress if needed
        // if (typeof config.onDownloadProgress === 'function') {
        //     request.addEventListener('progress', config.onDownloadProgress);
        // }
        //
        // // Not all browsers support upload events
        // if (typeof config.onUploadProgress === 'function' && request.upload) {
        //     request.upload.addEventListener('progress', config.onUploadProgress);
        // }
        //
        // if ('onloadend' in request) {
        //     // Use onloadend if available
        //     request.onloadend = onloadend;
        // } else {
        //     // Listen for ready state to emulate onloadend
        //     request.onreadystatechange = function handleLoad() {
        //         if (!request || request.readyState !== 4) {
        //             return;
        //         }
        //
        //         // The request errored out and we didn't get a response, this will be
        //         // handled by onerror instead
        //         // With one exception: request that using file: protocol, most browsers
        //         // will return status as 0 even though it's a successful request
        //         if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
        //             return;
        //         }
        //         // readystate handler is calling before onerror or ontimeout handlers,
        //         // so we should call onloadend on the next 'tick'
        //         setTimeout(onloadend);
        //     };
        // }
        //
        // // Handle browser request cancellation (as opposed to a manual cancellation)
        // request.onabort = function handleAbort() {
        //     if (!request) {
        //         return;
        //     }
        //
        //     reject(createError('Request aborted', config, 'ECONNABORTED', request));
        //
        //     // Clean up request
        //     request = null;
        // };
        //
        // // Handle low level network errors
        // request.onerror = function handleError() {
        //     // Real errors are hidden from us by the browser
        //     // onerror should only fire if it's a network error
        //     reject(createError('Network Error', config, null, request));
        //
        //     // Clean up request
        //     request = null;
        // };
        //
        // // Handle timeout
        // request.ontimeout = function handleTimeout() {
        //     var timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
        //     var transitional = config.transitional || defaults.transitional;
        //     if (config.timeoutErrorMessage) {
        //         timeoutErrorMessage = config.timeoutErrorMessage;
        //     }
        //     reject(createError(
        //         timeoutErrorMessage,
        //         config,
        //         transitional.clarifyTimeoutError ? 'ETIMEDOUT' : 'ECONNABORTED',
        //         request));
        //
        //     // Clean up request
        //     request = null;
        // };
        //
        // // Add xsrf header
        // // This is only done if running in a standard browser environment.
        // // Specifically not if we're in a web worker, or react-native.
        // if (utils.isStandardBrowserEnv()) {
        //     // Add xsrf header
        //     var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
        //         cookies.read(config.xsrfCookieName) :
        //         undefined;
        //
        //     if (xsrfValue) {
        //         requestHeaders[config.xsrfHeaderName] = xsrfValue;
        //     }
        // }
        //
        // // Add headers to the request
        // if ('setRequestHeader' in request) {
        //     utils.forEach(requestHeaders, function setRequestHeader(val, key) {
        //         if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
        //             // Remove Content-Type if data is undefined
        //             delete requestHeaders[key];
        //         } else {
        //             // Otherwise add header to the request
        //             request.setRequestHeader(key, val);
        //         }
        //     });
        // }
        //
        // // Add withCredentials to request if needed
        // if (!utils.isUndefined(config.withCredentials)) {
        //     request.withCredentials = !!config.withCredentials;
        // }
        //
        // // Add responseType to request if needed
        // if (responseType && responseType !== 'json') {
        //     request.responseType = config.responseType;
        // }
        //
        // // Handle progress if needed
        // if (typeof config.onDownloadProgress === 'function') {
        //     request.addEventListener('progress', config.onDownloadProgress);
        // }
        //
        // // Not all browsers support upload events
        // if (typeof config.onUploadProgress === 'function' && request.upload) {
        //     request.upload.addEventListener('progress', config.onUploadProgress);
        // }
        //
        // if (config.cancelToken || config.signal) {
        //     // Handle cancellation
        //     // eslint-disable-next-line func-names
        //     onCanceled = function (cancel) {
        //         if (!request) {
        //             return;
        //         }
        //         reject(!cancel || (cancel && cancel.type) ? new Cancel('canceled') : cancel);
        //         request.abort();
        //         request = null;
        //     };
        //
        //     config.cancelToken && config.cancelToken.subscribe(onCanceled);
        //     if (config.signal) {
        //         config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
        //     }
        // }
        //
        // if (!requestData) {
        //     requestData = null;
        // }

        let isHeadersJson = (headers) => headers && headers['content-type'] && String(headers['content-type']).indexOf('application/json') >= 0;

        if (typeof (GM_xmlhttpRequest) === 'undefined') {
            // use axios original code with noChange.
            // 使用axios原来方式调用
            // Send the request
            if (typeof (appConfig) !== 'undefined' && appConfig.isDebug) {
                console.log('use origin axios adapter; config: ', config);
            }
            originAdapter(config).then(suc => resolve(suc)).catch(err => reject(err));
            // resolve(requestData);
        } else {
            // after now, use GM_xmlhttpRequest to adapter axios request
            // 使用GM_xmlhttpRequest方式调用
            if (typeof (appConfig) !== 'undefined' && appConfig.isDebug) {
                console.log('use new axios adapter; config: ', config);
            }
            window.GM_xmlhttpRequest = typeof (GM_xmlhttpRequest) === 'undefined' ? undefined : GM_xmlhttpRequest;
            let url = config.url;
            let method = config.method.toUpperCase();
            let data = requestData;
            let headers = config.headers;
            if (!data) {
                data = null;
            } else {
                if (isHeadersJson(headers)) {
                    if (isJson(data)) {
                        data = JSON.parse(data);
                    }
                } else {
                    if (typeof (data) === 'undefined') {
                        data = null;
                    } else if (typeof (data) !== 'string') {
                        // 添加FormData支持
                        if (data instanceof FormData) {
                            // @see <a href="https://blog.csdn.net/weixin_34413802/article/details/88722992">前端通过axios和FormData实现文件上传功能遇到的坑</a>
                            // 让浏览器来继续设置这个值
                            delete headers['content-type'];
                        } else {
                            data = JSON.stringify(data);
                        }
                    }
                }
            }
            let param = {
                url,
                method,
                headers,
                data,
                timeout: config.timeout,
            };
            if (responseType) {
                // 支持blob下载
                param.responseType = responseType;
            }
            if (overrideMimeType) {
                // 支持form表单提交
                if (appConfig.isDebug) {
                    console.log('支持form表单提交');
                }
                param.overrideMimeType = overrideMimeType;
            }
            param.onerror = (error) => {
                reject(error)
            };
            param.ontimeout = (timeout) => {
                console.error('execute timeout! config=%o, timeout=%o', config, timeout);
                reject(timeout);
            };
            param.onload = (resp) => {
                let headers = {};
                let headArr = resp.responseHeaders.split("\n");
                headArr.forEach(head => {
                    if (head) {
                        let hArr = head.split(":");
                        let key = hArr.shift();
                        headers[key] = hArr.join().trim();
                    }
                });
                resp.headers = headers;
                let data;
                if (isHeadersJson(headers)) {
                    if (isJson(resp.responseText)) {
                        data = JSON.parse(resp.responseText);
                    } else {
                        if (typeof (appConfig) !== 'undefined' && appConfig.isDebug) {
                            let contentType = headers['content-type'];
                            console.error('响应非json文本,contentType: %s', contentType);
                        }
                        data = resp.responseText;
                    }
                } else {
                    // 支持 blob, json 请求
                    if (responseType && (responseType === 'blob' || responseType === 'json')) {
                        data = resp.response;
                    } else {
                        data = resp.responseText;
                    }
                }
                resp.data = data;
                resp.config = config;
                resp.request = param;

                // 参考 axios ["data", "status", "statusText", "headers", "config", "request"]
                // // Prepare the response
                // var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
                // var responseData = !responseType || responseType === 'text' || responseType === 'json' ?
                //     request.responseText : request.response;
                // var response = {
                //     data: responseData,
                //     status: request.status,
                //     statusText: request.statusText,
                //     headers: responseHeaders,
                //     config: config,
                //     request: request
                // };
                resolve(resp);
            };
            if (!!GM_xmlhttpRequest) {
                GM_xmlhttpRequest(param)
            } else {
                console.error("没有找到GM_xmlhttpRequest");
            }
        }
    });
};